﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace MinotaurPathfinder
{
    class Pathfinder
    {
        public const int bigNumber = int.MaxValue - 1;
        public const int gridSizeX = 64;
        public const int gridSizeY = 48;

        /*
         * 
         * Movements is an array of various directions.
         * 
         * */
        Point[] _movements;

        /*
         * 
         * Squares is an array of square objects.
         * 
         * */
        CompleteSquare[,] _squares = new CompleteSquare[Pathfinder.gridSizeX, Pathfinder.gridSizeY];
        public CompleteSquare[,] Squares
        {
            get { return _squares; }
            set { _squares = value; }
        }

        public Pathfinder()
        {
            InitMovements(8);
            ClearSquares();
        }

        public void InitMovements(int movementCount)
        {
            /*
             * 
             * Just do some initializations.
             * 
             * */
            if (movementCount == 4)
            {
                _movements = new Point[]
                {
                    new Point(0, -1),
                    new Point(1, 0),
                    new Point(0, 1),
                    new Point(-1, 0)
                };
            }
            else
            {
                _movements = new Point[]
                {
                    new Point(-1, -1),
                    new Point(0, -1),
                    new Point(1, -1),
                    new Point(1, 0),
                    new Point(1, 1),
                    new Point(0, 1),
                    new Point(-1, 1),
                    new Point(-1, 0)
                };
            }
        }

        public void ClearSquares()
        {
            /*
             * 
             * Reset every square.
             * 
             * */
            foreach (Point point in AllSquares())
            {
                _squares[point.X, point.Y] = new CompleteSquare();
            }
        }

        public void ClearLogic()
        {
            /*
             * 
             * Reset some information about the squares.
             * 
             * */
            foreach (Point point in AllSquares())
            {
                int x = point.X;
                int y = point.Y;
                _squares[x, y].DistanceSteps = 10000; // Pathfinder.bigNumber;
                _squares[x, y].IsPath = false;
            }
        }

        public void DrawBoard(BoardControl boardControl1, int vehicleID)
        {
            foreach (Point point in AllSquares())
            {
                int x = point.X;
                int y = point.Y;
                int num = _squares[x, y].DistanceSteps;
                Color setColor = Color.Gainsboro;
                int content = _squares[x, y].ContentCode;

                if (content == SquareContent.Empty)
                {
                    if (_squares[x, y].IsPath == true)
                    {
                        setColor = Color.LightBlue;
                    }
                    else
                    {
                        setColor = Color.White;
                    }
                }
                else
                {
                    if (content == SquareContent.StartLocation(vehicleID))
                    {
                        setColor = Color.Coral;
                    }
                    else if (content == SquareContent.FinishLocation(vehicleID))
                    {
                        setColor = Color.Blue;
                    }
                    else if(content == SquareContent.Wall)
                    {
                        setColor = Color.Gray;
                    }
                    else
                    {
                        setColor = Color.HotPink;
                    }
                }
                boardControl1.SetHighlight(setColor, x, y);
            }
            boardControl1.Invalidate();
        }

        public Point getMove(Point p1, Point p2)
        {
            return new Point(p1.X - p2.X, p1.Y - p2.Y);
        }

        public void Pathfind(int vehicleID)
        {
            /*
             * 
             * Find path from hero to monster. First, get coordinates
             * of hero.
             * 
             * */
            Point startingPoint = FindCode(SquareContent.StartLocation(vehicleID));
            int heroX = startingPoint.X;
            int heroY = startingPoint.Y;
            if (heroX == -1 || heroY == -1)
            {
                return;
            }
            /*
             * 
             * Hero starts at distance of 0.
             * 
             * */
            _squares[heroX, heroY].DistanceSteps = 0;

            while (true)
            {
                bool madeProgress = false;

                /*
                 * 
                 * Look at each square on the board.
                 * 
                 * */
                foreach (Point mainPoint in AllSquares())
                {
                    int x = mainPoint.X;
                    int y = mainPoint.Y;

                    /*
                     * 
                     * If the square is open, look through valid moves given
                     * the coordinates of that square.
                     * 
                     * */
                    if (SquareOpen(x, y))
                    {

                        int passHere = _squares[x, y].DistanceSteps;

                        foreach (Point movePoint in ValidMoves(x, y))
                        {
                            int newX = movePoint.X;
                            int newY = movePoint.Y;
                            int newPass;

                            if (getMove(mainPoint, movePoint).Equals(_movements[0])
                                || getMove(mainPoint, movePoint).Equals(_movements[2])
                                || getMove(mainPoint, movePoint).Equals(_movements[4])
                                || getMove(mainPoint, movePoint).Equals(_movements[6]))
                            {
                                newPass = passHere + 2;
                            }
                            else
                            {
                                newPass = passHere + 1;
                            }

                             

                            if (_squares[newX, newY].DistanceSteps > newPass)
                            {
                                _squares[newX, newY].DistanceSteps = newPass;
                                madeProgress = true;
                            }
                        }
                    }
                }
                if (!madeProgress)
                {
                    break;
                }
            }
        }

        static private bool ValidCoordinates(int x, int y)
        {
            /*
             * 
             * Our coordinates are constrained between 0 and 14.
             * 
             * */
            if (x < 0)
            {
                return false;
            }
            if (y < 0)
            {
                return false;
            }
            if (x > Pathfinder.gridSizeX - 1)
            {
                return false;
            }
            if (y > Pathfinder.gridSizeY - 1)
            {
                return false;
            }
            return true;
        }

        private bool SquareOpen(int x, int y)
        {
            /*
             * 
             * A square is open if it is not a wall.
             * 
             * */

            /*switch (_squares[x, y].ContentCode)
            {
                case SquareContent.Empty:
                    return true;
                case SquareContent.Hero:
                    return true;
                case SquareContent.Monster:
                    return true;
                case SquareContent.Wall:
                default:
                    return false;
            }*/
            return _squares[x, y].ContentCode != SquareContent.Wall;
        }

        private Point FindCode(int contentIn)
        {
            /*
             * 
             * Find the requested code and return the point.
             * 
             * */
            foreach (Point point in AllSquares())
            {
                if (_squares[point.X, point.Y].ContentCode == contentIn)
                {
                    return new Point(point.X, point.Y);
                }
            }
            return new Point(-1, -1);
        }

        public List<Point> HighlightPath(int vehicleID)
        {
            /*
             * 
             * Mark the path from monster to hero.
             * 
             * */

            List<Point> result = new List<Point>();

            
            Point startingPoint = FindCode(SquareContent.FinishLocation(vehicleID));

            result.Add(startingPoint);

            int pointX = startingPoint.X;
            int pointY = startingPoint.Y;
            if (pointX == -1 && pointY == -1)
            {
                return null;
            }



            while (true)
            {
                /*
                 * 
                 * Look through each direction and find the square
                 * with the lowest number of steps marked.
                 * 
                 * */
                Point lowestPoint = Point.Empty;
                int lowest = Pathfinder.bigNumber; //10000

                foreach (Point movePoint in ValidMoves(pointX, pointY))
                {
                    int count = _squares[movePoint.X, movePoint.Y].DistanceSteps;
                    if (count < lowest)
                    {
                        lowest = count;
                        lowestPoint.X = movePoint.X;
                        lowestPoint.Y = movePoint.Y;
                    }
                }
                if (lowest != Pathfinder.bigNumber) //10000
                {
                    /*
                     * 
                     * Mark the square as part of the path if it is the lowest
                     * number. Set the current position as the square with
                     * that number of steps.
                     * 
                     * */
                    _squares[lowestPoint.X, lowestPoint.Y].IsPath = true;
                    pointX = lowestPoint.X;
                    pointY = lowestPoint.Y;

                    //result.Add(new Point(pointX, pointY));
                    result.Insert(0, new Point(pointX, pointY));
                }
                else
                {
                    break;
                }

                if (_squares[pointX, pointY].ContentCode == SquareContent.StartLocation(vehicleID))
                {
                    /*
                     * 
                     * We went from monster to hero, so we're finished.
                     * 
                     * */
                    break;
                }
            }

            return result;
        }

        private static IEnumerable<Point> AllSquares()
        {
            /*
             * 
             * Return every point on the board in order.
             * 
             * */
            for (int x = 0; x < Pathfinder.gridSizeX; x++)
            {
                for (int y = 0; y < Pathfinder.gridSizeY; y++)
                {
                    yield return new Point(x, y);
                }
            }
        }

        private IEnumerable<Point> ValidMoves(int x, int y)
        {
            /*
             * 
             * Return each valid square we can move to.
             * 
             * */
            foreach (Point movePoint in _movements)
            {
                int newX = x + movePoint.X;
                int newY = y + movePoint.Y;

                if (ValidCoordinates(newX, newY) &&
                    SquareOpen(newX, newY))
                {
                    yield return new Point(newX, newY);
                }
            }
        }

        public static Point ExtractMovement(Point _from, Point _to)
        {
            return new Point(
                _to.X - _from.X,
                _to.Y - _from.Y);
        }
    }
}
